/*
 * $Id: PDFStream.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ $Date: 2007/08/26
 * 18:56:35 $ This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the License,
 * or (at your option) any later version. This library is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU Lesser General Public License for more details. You should have
 * received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
package pdf.handler;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.zip.DeflaterOutputStream;

/**
 * This class implements a PDF stream object. In PDF, streams contain data like
 * the graphic operators that render a page, or the pixels of an image.
 * <p>
 * In PDF, a stream can be compressed using several different methods, or left
 * uncompressed. Here we support both uncompressed, and FlateDecode as it's
 * supported by the java core.
 * 
 * @author Peter T Mount http://www.retep.org.uk/pdf/
 * @author Eric Z. Beard, ericzbeard@hotmail.com
 * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $
 */
public class PDFStream extends PDFObject implements Serializable
{

   /*
    * NOTE: The original class is the work of Peter T. Mount, who released it
    * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as
    * follows: The package name was changed to pdf.handler. The formatting was
    * changed a little bit. It is still licensed under the LGPL.
    */

   /**
    * This holds the stream's content.
    */
   transient ByteArrayOutputStream buf;

   /**
    * True if we will compress the stream in the pdf file
    */
   boolean deflate;

   /**
    * Constructs a plain stream.
    * <p>
    * By default, the stream will be compressed.
    */
   public PDFStream()
   {
      this(null);
   }

   /**
    * Constructs a stream. The supplied type is stored in the stream's header
    * and is used by other objects that extend the PDFStream class (like
    * PDFImage).
    * <p>
    * By default, the stream will be compressed.
    * 
    * @param type
    *            type for the stream
    * @see PDFImage
    */
   public PDFStream(String type)
   {
      super(type);
      buf = new ByteArrayOutputStream();

      // default deflate mode
      deflate = false;
   }

   /**
    * Returs true if the stream will be compressed.
    * 
    * @return true if compression is enabled
    */
   public boolean getDeflate()
   {
      return deflate;
   }

   /**
    * Returns the OutputStream that will append to this stream.
    * 
    * @return The stream for this object
    */
   public OutputStream getOutputStream()
   {
      return buf;
   }

   /**
    * This is for extenders, and provides access to the stream.
    * 
    * @return ByteArrayOutputStream containing the contents.
    */
   public ByteArrayOutputStream getStream()
   {
      return buf;
   }

   /**
    * Creates a PrintWriter that will append to this stream.
    * 
    * @return a PrintWriter to write to the stream
    */
   public PrintWriter getWriter()
   {
      return new PrintWriter(buf, true);
   }

   /**
    * This implements our own special Serialization for this object
    * <p>
    * Here we read the length of the stream's contents, then a byte array of
    * the contents. Then we recreate a new ByteArrayOutputStream. We have to do
    * this, as ByteArrayOutputStream is not serializable (hence the transient
    * tag).
    */
   private void readObject(java.io.ObjectInputStream in) throws IOException
   {
      int l = in.readInt();
      byte b[] = new byte[l];
      in.read(b, 0, l);
      buf = new ByteArrayOutputStream(l);
      buf.write(b);
   }

   /**
    * @param mode
    *            true will FlatDecode the stream
    */
   public void setDeflate(boolean mode)
   {
      // TODO: Restore the next line of code to allow deflate to occur.
      deflate = mode;
   }

   /**
    * @param os
    *            OutputStream to send the object to
    * @exception IOException
    *                on error
    */
   @Override
   public void write(OutputStream os) throws IOException
   {
      writeStart(os);
      writeStream(os);
      // Unlike most PDF objects, we dont call writeEnd(os) because we
      // contain a stream
   }

   // Why is this here? Did it have a specific purpose?

   /**
    * This implements our own special Serialization for this object.
    * <p>
    * Here we write the length of the stream's contents, then a byte array of
    * the contents. We have to do this, as ByteArrayOutputStream is not
    * serializable (hence the transient tag).
    */
   private void writeObject(java.io.ObjectOutputStream out) throws IOException
   {
      out.writeInt(buf.size());
      out.write(buf.toByteArray());
   }

   /**
    * This inserts the Streams length, then the actual stream, finally the end
    * of stream/object markers.
    * <p>
    * This is intended for anyone extending PDFStream, as objects containing
    * streams do no use writeEnd(), and they must be able to write the actual
    * stream.
    * 
    * @param os
    *            OutputStream to send the object to
    * @exception IOException
    *                on error
    */
   public void writeStream(OutputStream os) throws IOException
   {
      if (deflate)
      {
         ByteArrayOutputStream b = new ByteArrayOutputStream();
         DeflaterOutputStream dos = new DeflaterOutputStream(b);
         // ,new Deflater(Deflater.BEST_COMPRESSION,true));
         buf.writeTo(dos);
         dos.finish();
         dos.close();

         // FlatDecode is compatible with the java.util.zip.Deflater class
         os.write("/Filter /FlateDecode\n".getBytes());
         os.write("/Length ".getBytes());
         os.write(Integer.toString(b.size() + 1).getBytes());
         os.write("\n>>\nstream\n".getBytes());
         b.writeTo(os);
         os.write("\n".getBytes());
      }
      else
      {
         // This is a non-deflated stream
         os.write("/Length ".getBytes());
         os.write(Integer.toString(buf.size()).getBytes());
         os.write("\n>>\nstream\n".getBytes());
         buf.writeTo(os);
      }

      os.write("endstream\nendobj\n".getBytes());

      // Unlike most PDF objects, we dont call writeEnd(os) because we
      // contain a stream
   }

}
